<# .SYNOPSIS Configure Network Access security options using positional arguments - Secpol Policy Framework. .SCRIPTTYPE Computer Configuration .DESCRIPTION This script applies Network Access security option settings using positional arguments based on the PolicyDatabase array. Policies configured (13 total): 1. Network access: Allow anonymous SID/Name translation - 0=Disabled, 1=Enabled 2. Network access: Do not allow anonymous enumeration of SAM accounts - 0=Allow, 1=Restrict 3. Network access: Do not allow anonymous enumeration of SAM accounts and shares - 0=None, 1=No enumeration, 2=No enumeration without explicit permissions 4. Network access: Do not allow storage of passwords and credentials - 0=Allow, 1=Do not allow 5. Network access: Force logoff when logon hours expire - 0=Disabled, 1=Enabled 6. Network access: Let Everyone permissions apply to anonymous users - 0=Disabled, 1=Enabled 7. Network access: Named Pipes that can be accessed anonymously - Multi-string list 8. Network access: Remotely accessible registry paths - Multi-string list 9. Network access: Remotely accessible registry paths and sub-paths - Multi-string list 10. Network access: Restrict anonymous access to Named Pipes and Shares - 0=None, 1=Enabled 11. Network access: Restrict clients allowed to make remote calls to SAM - SDDL string 12. Network access: Shares that can be accessed anonymously - Multi-string list 13. Network access: Sharing and security model for local accounts - 0=Classic, 1=Guest only NOTE: Restricting anonymous access enhances security but may break some applications. Test thoroughly as these settings can impact network functionality. .PARAMETER PolicyValues JSON array string containing 13 policy values (in order). Use empty strings "" to skip policies. Format: '["value1","value2",...,"value13"]' .PARAMETER LogLevel Logging verbosity: Silent, Normal, Verbose, Debug .PARAMETER LogPath Custom log file path (optional) .PARAMETER WhatIf Preview changes without applying them .EXAMPLE .\Set-NetworkAccessSecurityOptions.ps1 '["0","1","1","1","1","0","","","","1","O:BAG:BAD:...","","0"]' Configures network access policies with restrictive anonymous access settings .EXAMPLE .\Set-NetworkAccessSecurityOptions.ps1 '["0","1","1","","","","","","","1","","",""]' -WhatIf Preview changes for anonymous access restrictions #> param( [Parameter(Position=0, ValueFromRemainingArguments=$true)] [string[]]$PolicyValuesArray = @("[]"), [ValidateSet('Silent','Normal','Verbose','Debug')] [string]$LogLevel = 'Normal', [string]$LogPath = $null, [switch]$WhatIf ) # Combine all arguments into a single PolicyValues string # First, try to get the original command line with proper quotes $PolicyValues = $null try { $currentPID = $PID Write-Host "Current Process ID: $currentPID" -ForegroundColor Cyan $process = Get-CimInstance Win32_Process -Filter "ProcessId = $currentPID" if ($process) { $commandLine = $process.CommandLine Write-Host "Full command line: $commandLine" -ForegroundColor Yellow if ($commandLine) { $scriptName = [System.IO.Path]::GetFileName($MyInvocation.MyCommand.Path) $escapedScriptName = [regex]::Escape($scriptName) $pattern = "-File\s+`"[^`"]*\\$escapedScriptName`"\s+(.+?)(?:\s+(?:-LogLevel|-LogPath|-WhatIf)|$)" Write-Host "Using regex pattern: $pattern" -ForegroundColor DarkGray if ($commandLine -match $pattern) { $rawArgument = $matches[1].Trim() Write-Host "Raw argument extracted: $rawArgument" -ForegroundColor Magenta if ($rawArgument -match '^"(.*)"$') { $PolicyValues = $matches[1] } else { $PolicyValues = $rawArgument } Write-Host "Extracted PolicyValues from command line: $PolicyValues" -ForegroundColor Green } else { Write-Host "Command line regex did not match. Command line: $commandLine" -ForegroundColor Red } } else { Write-Host "CommandLine property is null or empty" -ForegroundColor Red } } else { Write-Host "Failed to get process information for PID $currentPID" -ForegroundColor Red } } catch { Write-Host "Error extracting from command line: $($_.Exception.Message)" -ForegroundColor Red Write-Verbose "Could not extract from command line: $($_.Exception.Message)" } if (-not $PolicyValues) { $PolicyValues = if ($PolicyValuesArray.Count -gt 1) { $PolicyValuesArray -join '' } else { $PolicyValuesArray[0] } Write-Verbose "Using parameter-based PolicyValues: $PolicyValues" } # Input DB - Network Access Security Options # RegistryType: 4=DWORD, 1=String, 7=MultiString, 3=Binary $PolicyDatabase = @( @{ Name = "Network access: Allow anonymous SID/Name translation" KeyGroup = "[System Access]" Key = "LSAAnonymousNameLookup" RegistryType = 4 }, @{ Name = "Network access: Do not allow anonymous enumeration of SAM accounts" KeyGroup = "[Registry Values]" Key = "MACHINE\System\CurrentControlSet\Control\Lsa\RestrictAnonymousSAM" RegistryType = 4 }, @{ Name = "Network access: Do not allow anonymous enumeration of SAM accounts and shares" KeyGroup = "[Registry Values]" Key = "MACHINE\System\CurrentControlSet\Control\Lsa\RestrictAnonymous" RegistryType = 4 }, @{ Name = "Network access: Do not allow storage of passwords and credentials for network authentication" KeyGroup = "[Registry Values]" Key = "MACHINE\System\CurrentControlSet\Control\Lsa\DisableDomainCreds" RegistryType = 4 }, @{ Name = "Network access: Force logoff when logon hours expire" KeyGroup = "[Registry Values]" Key = "MACHINE\System\CurrentControlSet\Services\LanManServer\Parameters\EnableForcedLogoff" RegistryType = 4 }, @{ Name = "Network access: Let Everyone permissions apply to anonymous users" KeyGroup = "[Registry Values]" Key = "MACHINE\System\CurrentControlSet\Control\Lsa\EveryoneIncludesAnonymous" RegistryType = 4 }, @{ Name = "Network access: Named Pipes that can be accessed anonymously" KeyGroup = "[Registry Values]" Key = "MACHINE\System\CurrentControlSet\Services\LanManServer\Parameters\NullSessionPipes" RegistryType = 7 }, @{ Name = "Network access: Remotely accessible registry paths" KeyGroup = "[Registry Values]" Key = "MACHINE\System\CurrentControlSet\Control\SecurePipeServers\Winreg\AllowedExactPaths\Machine" RegistryType = 7 }, @{ Name = "Network access: Remotely accessible registry paths and sub-paths" KeyGroup = "[Registry Values]" Key = "MACHINE\System\CurrentControlSet\Control\SecurePipeServers\Winreg\AllowedPaths\Machine" RegistryType = 7 }, @{ Name = "Network access: Restrict anonymous access to Named Pipes and Shares" KeyGroup = "[Registry Values]" Key = "MACHINE\System\CurrentControlSet\Services\LanManServer\Parameters\RestrictNullSessAccess" RegistryType = 4 }, @{ Name = "Network access: Restrict clients allowed to make remote calls to SAM" KeyGroup = "[Registry Values]" Key = "MACHINE\System\CurrentControlSet\Control\Lsa\RestrictRemoteSAM" RegistryType = 1 }, @{ Name = "Network access: Shares that can be accessed anonymously" KeyGroup = "[Registry Values]" Key = "MACHINE\System\CurrentControlSet\Services\LanManServer\Parameters\NullSessionShares" RegistryType = 7 }, @{ Name = "Network access: Sharing and security model for local accounts" KeyGroup = "[Registry Values]" Key = "MACHINE\System\CurrentControlSet\Control\Lsa\ForceGuest" RegistryType = 4 } ) # Script-wide variables $script:LogFile = $null $script:StartTime = Get-Date $script:ProcessedCount = 0 $script:SuccessCount = 0 $script:FailureCount = 0 $script:SkippedCount = 0 # Initialize logging function Initialize-LogPath { if ($LogPath) { $logDir = Split-Path $LogPath -Parent if ($logDir -and -not (Test-Path $logDir)) { New-Item -ItemType Directory -Path $logDir -Force | Out-Null } return $LogPath } # Try to get agent directory, fallback to script directory $baseDir = $PSScriptRoot try { $registryPath = if ([Environment]::Is64BitOperatingSystem) { "HKLM:\SOFTWARE\WOW6432Node\AdventNet\DesktopCentral\DCAgent" } else { "HKLM:\SOFTWARE\AdventNet\DesktopCentral\DCAgent" } if (Test-Path $registryPath) { $agentDir = (Get-ItemProperty -Path $registryPath -Name "DCAgentInstallDir" -ErrorAction SilentlyContinue).DCAgentInstallDir if ($agentDir -and (Test-Path $agentDir)) { $baseDir = $agentDir } } } catch { # Silently fall back to script directory } $timestamp = Get-Date -Format 'yyyyMMdd_HHmmss' $logDir = Join-Path (Join-Path $baseDir "logs") "SecurityPolicies" if (-not (Test-Path $logDir)) { New-Item -ItemType Directory -Path $logDir -Force | Out-Null } return Join-Path $logDir "NetworkAccessSecurityOptions_$timestamp.log" } # Logging function with log levels function Write-Log { param( [string]$Message, [ValidateSet('INFO','SUCCESS','WARNING','ERROR','DEBUG','PROGRESS')] [string]$Level = 'INFO', [string]$Component = 'Main' ) $levelPriority = @{ 'Silent' = 0 'Normal' = 1 'Verbose' = 2 'Debug' = 3 } $messagePriority = @{ 'ERROR' = 0 'WARNING' = 0 'SUCCESS' = 1 'PROGRESS' = 1 'INFO' = 2 'Debug' = 3 } $timestamp = Get-Date -Format 'yyyy-MM-dd HH:mm:ss' $logEntry = "[$timestamp] [$Level] [$Component] $Message" # Always write to log file if ($script:LogFile) { Add-Content -Path $script:LogFile -Value $logEntry -ErrorAction SilentlyContinue } # Console output based on log level if ($levelPriority[$LogLevel] -ge $messagePriority[$Level]) { switch ($Level) { 'ERROR' { Write-Host $logEntry -ForegroundColor Red } 'WARNING' { Write-Host $logEntry -ForegroundColor Yellow } 'SUCCESS' { Write-Host $logEntry -ForegroundColor Green } 'DEBUG' { Write-Host $logEntry -ForegroundColor Cyan } 'PROGRESS'{ Write-Host $logEntry -ForegroundColor Magenta } default { Write-Host $logEntry } } } } # Progress logging function function Write-ProgressLog { param([string]$Message, [string]$Component = 'Progress') Write-Log -Message $Message -Level 'PROGRESS' -Component $Component } # Check if running as administrator function Test-Admin { $currentUser = [Security.Principal.WindowsIdentity]::GetCurrent() $principal = New-Object Security.Principal.WindowsPrincipal($currentUser) return $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) } # Initialize script function Initialize-Script { $script:LogFile = Initialize-LogPath Write-Log "========================================" -Level 'INFO' Write-Log "Network Access Security Options Configuration Started" -Level 'INFO' Write-Log "Script: $($MyInvocation.ScriptName)" -Level 'INFO' Write-Log "Log Level: $LogLevel" -Level 'INFO' Write-Log "WhatIf Mode: $WhatIf" -Level 'INFO' Write-Log "========================================" -Level 'INFO' if (-not (Test-Admin)) { Write-Log "ERROR: This script requires administrator privileges" -Level 'ERROR' exit 1 } } # Import INF file into structured data function Import-InfFile { param([string]$Path) if (-not (Test-Path $Path)) { Write-Log "INF file not found: $Path" -Level 'ERROR' return $null } $infData = @{} $currentSection = $null Get-Content $Path -Encoding Unicode | ForEach-Object { $line = $_.Trim() # Skip empty lines and comments if ([string]::IsNullOrWhiteSpace($line) -or $line.StartsWith(';')) { return } # Section header if ($line -match '^\[(.+)\]$') { $currentSection = $matches[1] if (-not $infData.ContainsKey($currentSection)) { $infData[$currentSection] = @{} } return } # Key-value pair if ($currentSection -and $line -match '^(.+?)\s*=\s*(.*)$') { $key = $matches[1].Trim() $value = $matches[2].Trim() $infData[$currentSection][$key] = $value } } return $infData } # Write INF data back to file function Write-InfFile { param( [hashtable]$Data, [string]$Path ) $output = @() $output += "[Unicode]" $output += "Unicode=yes" # Ensure [Version] section is first if ($Data.ContainsKey('Version')) { $output += "[Version]" foreach ($key in $Data['Version'].Keys) { $output += "$key=$($Data['Version'][$key])" } } # Write other sections foreach ($section in ($Data.Keys | Where-Object { $_ -notin @('Unicode','Version') } | Sort-Object)) { $output += "[$section]" foreach ($key in ($Data[$section].Keys | Sort-Object)) { $output += "$key=$($Data[$section][$key])" } } $output | Out-File -FilePath $Path -Encoding unicode -Force } # Set individual secpol row function Set-SecpolRow { param( [string]$Name, [string]$KeyGroup, [string]$Key, [string]$Value, [int]$RegistryType, [hashtable]$PolicyData ) if ([string]::IsNullOrWhiteSpace($Value)) { Write-Log "Skipping ''$Name'' - No value provided" -Level 'WARNING' -Component $Name $script:SkippedCount++ return $false } # Special handling for string values (RegistryType = 1 or 7): Convert newlines to commas for INF format compatibility if ($RegistryType -eq 1 -or $RegistryType -eq 7) { if ($Value -match '(\r\n|\n|\r)') { # Replace various newline formats with commas $Value = $Value -replace '(\r\n|\n|\r)', ',' # Remove any trailing commas $Value = $Value.TrimEnd(',') Write-Log "Converted multi-line text to comma-separated format for INF compatibility" -Level 'DEBUG' -Component $Name } } Write-ProgressLog "Processing: $Name" -Component $Name Write-Log "Setting: $Name = $Value" -Level 'INFO' -Component $Name Write-Log "Location: $KeyGroup\$Key" -Level 'DEBUG' -Component $Name if ($WhatIf) { Write-Log "[WhatIf] Would set $KeyGroup\$Key = $Value" -Level 'INFO' -Component $Name $script:SuccessCount++ return $true } try { # Update policy data with proper registry type format $sectionName = $KeyGroup.Trim('[',']') if (-not $PolicyData.ContainsKey($sectionName)) { $PolicyData[$sectionName] = @{} } if($sectionName -eq 'Registry Values' ) { # Format: key=RegistryType,Value (e.g., key=4,1 for DWORD with value 1) $PolicyData[$sectionName][$Key] = "$RegistryType,$Value" }else{ $PolicyData[$sectionName][$Key] = $Value } Write-Log "Successfully configured: $Name" -Level 'SUCCESS' -Component $Name $script:SuccessCount++ return $true } catch { Write-Log "Failed to configure ''$Name'': $($_.Exception.Message)" -Level 'ERROR' -Component $Name $script:FailureCount++ return $false } } # Save all changes to security policy # Parse PolicyValues array string to array function Parse-PolicyValuesArray { param([string]$ArrayString) # Check if it looks like JSON format if ($ArrayString -match '^\s*\[.*\]\s*$') { try { # Fix common JSON format issues: unquoted alphanumeric values $FixedArrayString = $ArrayString -replace '\[(\w+)(,|])', '["$1"$2' -replace ',(\w+)(,|])', ',"$1"$2' # Parse the JSON-like array string $Arguments = ConvertFrom-Json $FixedArrayString Write-Log "Successfully parsed policy values array: $($Arguments.Count) values provided" -Level Info return $Arguments } catch { Write-Log "Failed to parse PolicyValues array string: $($_.Exception.Message)" -Level Error Write-Log "Expected format: '[\"value1\",\"value2\",\"value3\"]'" -Level Warning return @() } } else { # Not JSON format - use PolicyValuesArray directly as positional arguments Write-Log "Using positional arguments format (non-JSON): $($PolicyValuesArray.Count) values provided" -Level Info return $PolicyValuesArray } } # Parse PolicyValues array string to get individual arguments $Arguments = Parse-PolicyValuesArray -ArrayString $PolicyValues function Save-SecpolChanges { param([hashtable]$PolicyData) $exportPath = Join-Path $PSScriptRoot "secpol_export.inf" $modifiedPath = Join-Path $PSScriptRoot "secpol_modified.inf" try { Write-ProgressLog "Saving changes to security policy database..." # Write modified policy Write-InfFile -Data $PolicyData -Path $modifiedPath Write-Log "Modified policy written to: $modifiedPath" -Level 'DEBUG' # Apply the policy Write-Log "Applying security policy configuration..." -Level 'INFO' $seceditOutput = secedit /configure /db secedit.sdb /cfg $modifiedPath /areas SECURITYPOLICY 2>&1 if ($LASTEXITCODE -eq 0) { Write-Log "Security policy applied successfully" -Level 'SUCCESS' return $true } else { Write-Log "Secedit returned exit code: $LASTEXITCODE" -Level 'ERROR' Write-Log "Secedit output: $seceditOutput" -Level 'ERROR' return $false } } catch { Write-Log "Error saving security policy: $($_.Exception.Message)" -Level 'ERROR' return $false } } # Main execution Initialize-Script try { if ($PolicyDatabase.Count -eq 0) { Write-Log "Policy database is empty. Please populate the PolicyDatabase array." -Level 'WARNING' exit 0 } # Export current policy $exportPath = Join-Path $PSScriptRoot "secpol_export.inf" Write-Log "Exporting current security policy..." -Level 'INFO' secedit /export /cfg $exportPath /areas SECURITYPOLICY | Out-Null # Import current policy data $policyData = Import-InfFile -Path $exportPath if (-not $policyData) { Write-Log "Failed to import current security policy" -Level 'ERROR' exit 1 } # Process each policy with its corresponding argument for ($i = 0; $i -lt $PolicyDatabase.Count; $i++) { $policy = $PolicyDatabase[$i] $value = if ($i -lt $Arguments.Count) { $Arguments[$i] } else { $null } $script:ProcessedCount++ Set-SecpolRow -Name $policy.Name -KeyGroup $policy.KeyGroup -Key $policy.Key -Value $value -RegistryType $policy.RegistryType -PolicyData $policyData } # Save all changes if not in WhatIf mode and there were successful changes if (-not $WhatIf -and $script:SuccessCount -gt 0) { Save-SecpolChanges -PolicyData $policyData } } catch { Write-Log "Critical error in main execution: $($_.Exception.Message)" -Level 'ERROR' Write-Log "Stack trace: $($_.ScriptStackTrace)" -Level 'DEBUG' } finally { # Summary $duration = (Get-Date) - $script:StartTime Write-Log "========================================" -Level 'INFO' Write-Log "Execution Summary:" -Level 'INFO' Write-Log " Total Processed: $($script:ProcessedCount)" -Level 'INFO' Write-Log " Successful: $($script:SuccessCount)" -Level 'INFO' Write-Log " Failed: $($script:FailureCount)" -Level 'INFO' Write-Log " Skipped: $($script:SkippedCount)" -Level 'INFO' Write-Log " Duration: $($duration.TotalSeconds) seconds" -Level 'INFO' Write-Log "========================================" -Level 'INFO' }